package main

import (
	"code.google.com/p/goblinite/lib"
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
)

const command = "cal"

func usage() {
	fmt.Fprint(os.Stderr, "usage: ", command, " [ month ] [ year ]\n")
	os.Exit(2)
}

var errBadValue = errors.New("bad argument")

func main() {
	if len(os.Args) > 3 {
		usage()
	}
	switch len(os.Args) {
	case 1:
		printMonth(time.Now().Month(), time.Now().Year())
	case 2:
		if month, ok := parseMonth(os.Args[1]); ok {
			printMonth(month, time.Now().Year())
		} else if year, ok := parseYear(os.Args[1]); ok {
			printYear(year)
		} else {
			lib.Fatal(command, errBadValue)
		}
	case 3:
		if month, ok := parseMonth(os.Args[1]); ok {
			if year, ok := parseYear(os.Args[2]); ok {
				printMonth(month, year)
				break
			}
		}
		lib.Fatal(command, errBadValue)
	}
	os.Exit(0)
}

func parseYear(value string) (int, bool) {

	if year, err := strconv.Atoi(value); err == nil &&
		year >= 1 && year <= 9999 {
		return year, true
	}
	return 0, false
}

var dict = make(map[string]time.Month)

func init() {
	for month := time.January; month <= time.December; month++ {
		dict[strings.ToLower(month.String())] = month
		dict[strings.ToLower(month.String()[:3])] = month
	}
}

func parseMonth(value string) (time.Month, bool) {

	if month, ok := dict[value]; ok {
		return month, true
	}
	if number, err := strconv.Atoi(value); err == nil &&
		number >= int(time.January) && number <= int(time.December) {
		return time.Month(number), true
	}
	return time.Month(0), false
}

var weekHeader = " S  M Tu  W Th  F  S"

var months = map[time.Month]int{
	time.January: 31, time.February: 29, time.March: 31,
	time.April: 30, time.May: 31, time.June: 30,
	time.July: 31, time.August: 31, time.September: 30,
	time.October: 31, time.November: 30, time.December: 31}

func daysInMonth(month time.Month) {

	if month == 2 {
		//
	}
	// return month[month]

}

// BUG: sep 1752
// BUG: leap years
// BUG: cal (9, gnu) prints empty lines for "missing" weeks
func printMonth(month time.Month, year int) {

	first := firstDayOfMonth(month, year)
	fmt.Printf("   %s %d\n%s\n", month, year, weekHeader)
	for day := time.Sunday; day < first; day++ {
		fmt.Print("   ")
	}
	var day int
	last := int(first) + months[month] - 1
	for day = int(first); day <= last; day++ {
		fmt.Printf("%2d", day-int(first)+1)
		if day%7 == int(time.Saturday) {
			fmt.Println()
		} else if day != last {
			fmt.Print(" ")
		}
	}
	fmt.Println()
}

// TODO: implement this
func printYear(year int) {
	fmt.Println(year)
}

// stolen from
// https://bitbucket.org/amertune/goblin/src/208244a38a450302f794ac7a24007252890acafb/cal.go
func firstDayOfMonth(month time.Month, year int) time.Weekday {
	a := (14 - int(month)) / 12
	y := year - a
	m := int(month) + 12*a - 2

	d := 6 + 5*y/4 + 31*m/12
	if year <= 1752 {
		return time.Weekday(d % 7)
	}
	return time.Weekday((d + 2 - y/100 + y/400) % 7)
}
